// 15 面向对象程序设计
/**
 * 面向对象程序设计基于三个基本概念：数据抽象、继承和动态绑定。第7章已经介绍了数据抽象的知识，本章将介绍继承和动态绑定。
 * 继承和动态绑定对程序的编写有两方面的影响：一是我们可以更容易地定义与其他类相似但不完全相同的新类；二是在使用这些彼此相似的类编写程序时，我们可以在一定程度上忽略掉它们的区别。
 * 在很多程序中都存在着一些相互关联但是有细微差别的概念。
 * 例如，书店中不同书籍的定价策略可能不同：有的书籍按原价销售，有的则打折销售。有时，我们给那些购买书籍超过一定数量的顾客打折；另一些时候，则只对前多少本销售的书籍打折，之后就调回原价，等等。
 * 面向对象的程序设计（OOP）适用于这类应用。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;
#include "../Chapter13/13.5.cc" // 不能编译，因为重复定义的main函数
#include "../Chapter12/12.1.6.cc" // 不能编译，因为重复定义的main函数
#include <functional>

// 为了对之前提到的不同定价策略建模，我们首先定义一个名为Quote的类，并将它作为层次关系中的基类。
// Quote的对象表示按原价销售的书籍。Quote派生出另一个名为Bulk_quote的类，它表示可以打折销售的书籍。
class Quote
{
public:
    // isbn（ ），返回书籍的ISBN编号。该操作不涉及派生类的特殊性，因此只定义在Quote类中。
    std::string isbn() const;
    // net_price（size_t），返回书籍的实际销售价格，前提是用户购买该书的数量达到一定标准。这个操作显然是类型相关的，Quote和Bulk_quote都应该包含该函数。
    virtual double net_price(std::size_t n) const;
    // 在C++语言中，基类将类型相关的函数与派生类不做改变直接继承的函数区分对待。对于某些函数，基类希望它的派生类各自定义适合自身的版本，此时基类就将这些函数声明成虚函数（virtual function）。
};
// 派生类必须通过使用类派生列表（class derivation list）明确指出它是从哪个（哪些）基类继承而来的。
// 类派生列表的形式是：首先是一个冒号，后面紧跟以逗号分隔的基类列表，其中每个基类前面可以有访问说明符：
class Bulk_quote : public Quote // Bulk_quote继承了Quote
{
    double net_price(std::size_t) const override; // 形参列表后增加override关键字
};
// 计算并打印销售给定数量的某种书籍所得的费用
// 使用基类的引用（或指针）调用一个虚函数时将发生动态绑定
double print_total(ostream &os, const Quote &item, size_t n)
{
    // 根据传入item形参的对象类型调用Quote::net_price或者Bulk_quote::net_price，使用动态绑定
    double ret = item.net_price(n);
    os << "ISBN: " << item.isbn() // 调用Quote::isbin
       << " # sold: " << n << " total due: " << ret << endl;
    return ret;
}

int main()
{
    // 15.1 OOP概述
    // 面向对象程序设计（object-oriented programming）的核心思想是数据抽象、继承和动态绑定。
    // 通过使用数据抽象，我们可以将类的接口与实现分离（见第7章）；
    // 使用继承，可以定义相似的类型并对其相似关系建模；
    // 使用动态绑定，可以在一定程度上忽略相似类型的区别，而以统一的方式使用它们的对象。

    // 继承
    // 通过继承（inheritance）联系在一起的类构成一种层次关系。
    // 通常在层次关系的根部有一个基类（base class），其他类则直接或间接地从基类继承而来，这些继承得到的类称为派生类（derived class）。
    // 基类负责定义在层次关系中所有类共同拥有的成员，而每个派生类定义各自特有的成员。
    // 在C++语言中，基类将类型相关的函数与派生类不做改变直接继承的函数区分对待。对于某些函数，基类希望它的派生类各自定义适合自身的版本，此时基类就将这些函数声明成虚函数（virtual function）。
    // 派生类必须在其内部对所有重新定义的虚函数进行声明。派生类可以在这样的函数之前加上virtual关键字，但是并不是非得这么做。
    // 出于15.3节（第538页）将要解释的原因，C++11新标准允许派生类显式地注明它将使用哪个成员函数改写基类的虚函数，具体措施是在该函数的形参列表之后增加一个override关键字。

    // 动态绑定
    // 通过使用动态绑定（dynamic binding），我们能用同一段代码分别处理Quote和Bulk_quote的对象。
    // 因为在上述过程中函数的运行版本由实参决定，即在运行时选择函数的版本，所以动态绑定有时又被称为运行时绑定（run-time binding）。
    // ******在C++语言中，当我们使用基类的引用（或指针）调用一个虚函数时将发生动态绑定。******

    return 0;
}